package gapidt.analysis.detectors;

import gapidt.analysis.APIDesignAnalysisUtils;
import gapidt.analysis.ASTUtils;
import gapidt.analysis.DesignProblem;
import gapidt.analysis.IDesignProblemsDetector;
import gapidt.analysis.designproblems.UndercoverFaultInformation;
import gapidt.plugin.AnalysisResultsManager;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;

public class UndercoverFaultInformationDetector extends IDesignProblemsDetector<IMethod> {
	
	private static String[] undercoverFaultNames = {
		"ping", "error", "errors", "fault", "faults", "fail", "fails",
		"exception", "exceptions", "overflow", "mistake", "misplay"};

	private List<List<String>> undercoverFaultSets;

	public UndercoverFaultInformationDetector(){
		super(IMethod.class);
		this.undercoverFaultSets = new ArrayList<>();
		List<String> set = new ArrayList<String>();
		set.add("code");
		set.add("message");
		this.undercoverFaultSets.add(set);	
	}
	
	public static String[] getUndercoverFaultNames() {
		return undercoverFaultNames;
	}

	public static void setUndercoverFaultNames(String[] undercoverFaultNames) {
		UndercoverFaultInformationDetector.undercoverFaultNames = undercoverFaultNames;
	}
	
	@Override
	public List<DesignProblem<?>> detectProblems(IMethod method){
		
		List<DesignProblem<?>> designProblems = new ArrayList<DesignProblem<?>>();
		
		if(!APIDesignAnalysisUtils.isServiceMethod(method)){
			return designProblems;
		}
		
		MethodDeclaration methodDeclaration = ASTUtils.parse(method);

		if(methodDeclaration.getReturnType2() != null){
			ITypeBinding returnTypeBinding = methodDeclaration.getReturnType2().resolveBinding();
			if(returnTypeBinding != null){
				designProblems.addAll(this.detectProblemsByWord(returnTypeBinding, method));
				designProblems.addAll(this.detectProblemsBySet(returnTypeBinding, method));
			}
		}
		return designProblems;
	}

	private List<DesignProblem<?>> detectProblemsByWord(ITypeBinding returnTypeBinding, IMethod method){
		
		List<DesignProblem<?>> designProblems = new ArrayList<DesignProblem<?>>();
		
		for(IVariableBinding typeAttribute : returnTypeBinding.getDeclaredFields()) {
			if(isUndercoverFaultName(typeAttribute.getName())) {
				designProblems.add(new UndercoverFaultInformation(method, typeAttribute.getName()));
				AnalysisResultsManager.getInstance().addDesigProblemCount(getClass().getSimpleName());
			}
		}
		return designProblems;
	}

	private List<DesignProblem<?>> detectProblemsBySet(ITypeBinding returnTypeBinding, IMethod method){
		
		List<DesignProblem<?>> designProblems = new ArrayList<DesignProblem<?>>();
		List<String> fields = new ArrayList<String>();
		for(IVariableBinding typeAttribute : returnTypeBinding.getDeclaredFields()) {
			fields.add(typeAttribute.getName());
		}
		
		for(List<String> undercoverFaultSet : this.undercoverFaultSets) {			
			int count = 0;			
			for(String anUndercoverFault : undercoverFaultSet) {							
				for (String field : fields) {				
					if( field.toLowerCase().contains(anUndercoverFault.toLowerCase())) {
						count++;
						break;
					}				
				}		
			}
			if (undercoverFaultSet.size() == count){
				designProblems.add(new UndercoverFaultInformation(method, undercoverFaultSet.toString()));
				AnalysisResultsManager.getInstance().addDesigProblemCount(getClass().getSimpleName());
			}
		}
		return designProblems;
	}

	private static boolean isUndercoverFaultName(String name){
		String normalizedName = normalizeName(name);
		for(String undercoverFaultName : undercoverFaultNames){
			String normalizedUndercoverFaultName = normalizeName(undercoverFaultName);
//			String normalizedUndercoverFaultName = normalizeTypeName(undercoverFaultName); 
			if(!normalizedUndercoverFaultName.equals("") && normalizedUndercoverFaultName.contains(normalizedName))
				return true;
		}
		return false;
	}
	
	private static String normalizeName(String name){
		if(name != null)
			return name.toUpperCase();
		return "";
	}
	
	private static String normalizeTypeName(String typeName){
		if(typeName != null)
			return typeName.replaceAll(" ", "");
		return "";
	}		
}
